/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-* This Source Code Form is subject to the terms of the Mozilla Public* License, v. 2.0. If a copy of the MPL was not distributed with this* file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"BasicCompositor.h"#include"BasicLayersImpl.h" // for FillRectWithMask#include"TextureHostBasic.h"#include"mozilla/layers/Effects.h"#include"nsIWidget.h"#include"gfx2DGlue.h"#include"mozilla/gfx/2D.h"#include"mozilla/gfx/gfxVars.h"#include"mozilla/gfx/Helpers.h"#include"mozilla/gfx/Tools.h"#include"mozilla/gfx/ssse3-scaler.h"#include"mozilla/layers/ImageDataSerializer.h"#include"mozilla/SSE.h"#include"gfxUtils.h"#include"YCbCrUtils.h"#include<algorithm>#include"ImageContainer.h"#include"gfxPrefs.h"namespacemozilla{usingnamespacemozilla::gfx;namespacelayers{classDataTextureSourceBasic:publicDataTextureSource,publicTextureSourceBasic{public:virtualconstchar*Name()constoverride{return"DataTextureSourceBasic";}explicitDataTextureSourceBasic(DataSourceSurface*aSurface):mSurface(aSurface),mWrappingExistingData(!!aSurface){}virtualDataTextureSource*AsDataTextureSource()override{// If the texture wraps someone else's memory we'd rather not use it as// a DataTextureSource per say (that is call Update on it).returnmWrappingExistingData?nullptr:this;}virtualTextureSourceBasic*AsSourceBasic()override{returnthis;}virtualgfx::SourceSurface*GetSurface(DrawTarget*aTarget)override{returnmSurface;}SurfaceFormatGetFormat()constoverride{returnmSurface?mSurface->GetFormat():gfx::SurfaceFormat::UNKNOWN;}virtualIntSizeGetSize()constoverride{returnmSurface?mSurface->GetSize():gfx::IntSize(0,0);}virtualboolUpdate(gfx::DataSourceSurface*aSurface,nsIntRegion*aDestRegion=nullptr,gfx::IntPoint*aSrcOffset=nullptr)override{MOZ_ASSERT(!mWrappingExistingData);if(mWrappingExistingData){returnfalse;}mSurface=aSurface;returntrue;}virtualvoidDeallocateDeviceData()override{mSurface=nullptr;SetUpdateSerial(0);}public:RefPtr<gfx::DataSourceSurface>mSurface;boolmWrappingExistingData;};/** * WrappingTextureSourceYCbCrBasic wraps YUV format BufferTextureHost to defer * yuv->rgb conversion. The conversion happens when GetSurface is called. */classWrappingTextureSourceYCbCrBasic:publicDataTextureSource,publicTextureSourceBasic{public:virtualconstchar*Name()constoverride{return"WrappingTextureSourceYCbCrBasic";}explicitWrappingTextureSourceYCbCrBasic(BufferTextureHost*aTexture):mTexture(aTexture),mSize(aTexture->GetSize()),mNeedsUpdate(true){mFromYCBCR=true;}virtualDataTextureSource*AsDataTextureSource()override{returnthis;}virtualTextureSourceBasic*AsSourceBasic()override{returnthis;}virtualWrappingTextureSourceYCbCrBasic*AsWrappingTextureSourceYCbCrBasic()override{returnthis;}virtualgfx::SourceSurface*GetSurface(DrawTarget*aTarget)override{if(mSurface&&!mNeedsUpdate){returnmSurface;}MOZ_ASSERT(mTexture);if(!mTexture){returnnullptr;}if(!mSurface){mSurface=Factory::CreateDataSourceSurface(mSize,gfx::SurfaceFormat::B8G8R8X8);}if(!mSurface){returnnullptr;}MOZ_ASSERT(mTexture->GetBufferDescriptor().type()==BufferDescriptor::TYCbCrDescriptor);MOZ_ASSERT(mTexture->GetSize()==mSize);mSurface=ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(mTexture->GetBuffer(),mTexture->GetBufferDescriptor().get_YCbCrDescriptor(),mSurface);mNeedsUpdate=false;returnmSurface;}SurfaceFormatGetFormat()constoverride{returngfx::SurfaceFormat::B8G8R8X8;}virtualIntSizeGetSize()constoverride{returnmSize;}virtualboolUpdate(gfx::DataSourceSurface*aSurface,nsIntRegion*aDestRegion=nullptr,gfx::IntPoint*aSrcOffset=nullptr)override{returnfalse;}virtualvoidDeallocateDeviceData()override{mTexture=nullptr;mSurface=nullptr;SetUpdateSerial(0);}virtualvoidUnbind()override{mNeedsUpdate=true;}voidSetBufferTextureHost(BufferTextureHost*aTexture)override{mTexture=aTexture;mNeedsUpdate=true;}voidConvertAndScale(constSurfaceFormat&aDestFormat,constIntSize&aDestSize,unsignedchar*aDestBuffer,int32_taStride){MOZ_ASSERT(mTexture);if(!mTexture){return;}MOZ_ASSERT(mTexture->GetBufferDescriptor().type()==BufferDescriptor::TYCbCrDescriptor);MOZ_ASSERT(mTexture->GetSize()==mSize);ImageDataSerializer::ConvertAndScaleFromYCbCrDescriptor(mTexture->GetBuffer(),mTexture->GetBufferDescriptor().get_YCbCrDescriptor(),aDestFormat,aDestSize,aDestBuffer,aStride);}public:BufferTextureHost*mTexture;constgfx::IntSizemSize;RefPtr<gfx::DataSourceSurface>mSurface;boolmNeedsUpdate;};BasicCompositor::BasicCompositor(CompositorBridgeParent*aParent,widget::CompositorWidget*aWidget):Compositor(aWidget,aParent),mIsPendingEndRemoteDrawing(false){MOZ_COUNT_CTOR(BasicCompositor);mMaxTextureSize=Factory::GetMaxSurfaceSize(gfxVars::ContentBackend());}BasicCompositor::~BasicCompositor(){MOZ_COUNT_DTOR(BasicCompositor);}boolBasicCompositor::Initialize(nsCString*constout_failureReason){returnmWidget?mWidget->InitCompositor(this):false;};int32_tBasicCompositor::GetMaxTextureSize()const{returnmMaxTextureSize;}voidBasicCompositingRenderTarget::BindRenderTarget(){if(mClearOnBind){mDrawTarget->ClearRect(Rect(0,0,mSize.width,mSize.height));mClearOnBind=false;}}voidBasicCompositor::DetachWidget(){if(mWidget){if(mIsPendingEndRemoteDrawing){// Force to end previous remote drawing.TryToEndRemoteDrawing(/* aForceToEnd */true);MOZ_ASSERT(!mIsPendingEndRemoteDrawing);}mWidget->CleanupRemoteDrawing();}Compositor::DetachWidget();}TextureFactoryIdentifierBasicCompositor::GetTextureFactoryIdentifier(){TextureFactoryIdentifierident(LayersBackend::LAYERS_BASIC,XRE_GetProcessType(),GetMaxTextureSize());returnident;}already_AddRefed<CompositingRenderTarget>BasicCompositor::CreateRenderTarget(constIntRect&aRect,SurfaceInitModeaInit){MOZ_ASSERT(aRect.width!=0&&aRect.height!=0,"Trying to create a render target of invalid size");if(aRect.width*aRect.height==0){returnnullptr;}RefPtr<DrawTarget>target=mDrawTarget->CreateSimilarDrawTarget(aRect.Size(),SurfaceFormat::B8G8R8A8);if(!target){returnnullptr;}RefPtr<BasicCompositingRenderTarget>rt=newBasicCompositingRenderTarget(target,aRect);returnrt.forget();}already_AddRefed<CompositingRenderTarget>BasicCompositor::CreateRenderTargetFromSource(constIntRect&aRect,constCompositingRenderTarget*aSource,constIntPoint&aSourcePoint){MOZ_CRASH("GFX: Shouldn't be called!");returnnullptr;}already_AddRefed<CompositingRenderTarget>BasicCompositor::CreateRenderTargetForWindow(constLayoutDeviceIntRect&aRect,constLayoutDeviceIntRect&aClearRect,BufferModeaBufferMode){MOZ_ASSERT(mDrawTarget);MOZ_ASSERT(aRect.width!=0&&aRect.height!=0,"Trying to create a render target of invalid size");if(aRect.width*aRect.height==0){returnnullptr;}RefPtr<BasicCompositingRenderTarget>rt;IntRectrect=aRect.ToUnknownRect();if(aBufferMode!=BufferMode::BUFFER_NONE){RefPtr<DrawTarget>target=mWidget->GetBackBufferDrawTarget(mDrawTarget,aRect,aClearRect);if(!target){returnnullptr;}MOZ_ASSERT(target!=mDrawTarget);rt=newBasicCompositingRenderTarget(target,rect);}else{IntRectwindowRect=rect;// Adjust bounds rect to account for new origin at (0, 0).if(windowRect.Size()!=mDrawTarget->GetSize()){windowRect.ExpandToEnclose(IntPoint(0,0));}rt=newBasicCompositingRenderTarget(mDrawTarget,windowRect);if(!aClearRect.IsEmpty()){IntRectclearRect=aRect.ToUnknownRect();mDrawTarget->ClearRect(Rect(clearRect-rt->GetOrigin()));}}returnrt.forget();}already_AddRefed<DataTextureSource>BasicCompositor::CreateDataTextureSource(TextureFlagsaFlags){RefPtr<DataTextureSourceBasic>result=newDataTextureSourceBasic(nullptr);if(aFlags&TextureFlags::RGB_FROM_YCBCR){result->mFromYCBCR=true;}returnresult.forget();}already_AddRefed<DataTextureSource>BasicCompositor::CreateDataTextureSourceAround(DataSourceSurface*aSurface){RefPtr<DataTextureSource>result=newDataTextureSourceBasic(aSurface);returnresult.forget();}already_AddRefed<DataTextureSource>BasicCompositor::CreateDataTextureSourceAroundYCbCr(TextureHost*aTexture){BufferTextureHost*bufferTexture=aTexture->AsBufferTextureHost();MOZ_ASSERT(bufferTexture);if(!bufferTexture){returnnullptr;}RefPtr<DataTextureSource>result=newWrappingTextureSourceYCbCrBasic(bufferTexture);returnresult.forget();}boolBasicCompositor::SupportsEffect(EffectTypesaEffect){returnaEffect!=EffectTypes::YCBCR&&aEffect!=EffectTypes::COMPONENT_ALPHA;}boolBasicCompositor::SupportsLayerGeometry()const{returngfxPrefs::BasicLayerGeometry();}staticRefPtr<gfx::Path>BuildPathFromPolygon(constRefPtr<DrawTarget>&aDT,constgfx::Polygon&aPolygon){MOZ_ASSERT(!aPolygon.IsEmpty());RefPtr<PathBuilder>pathBuilder=aDT->CreatePathBuilder();constnsTArray<Point4D>&points=aPolygon.GetPoints();pathBuilder->MoveTo(points[0].As2DPoint());for(size_ti=1;i<points.Length();++i){pathBuilder->LineTo(points[i].As2DPoint());}pathBuilder->Close();returnpathBuilder->Finish();}staticvoidDrawSurface(gfx::DrawTarget*aDest,constgfx::Rect&aDestRect,constgfx::Rect&/* aClipRect */,constgfx::Color&aColor,constgfx::DrawOptions&aOptions,gfx::SourceSurface*aMask,constgfx::Matrix*aMaskTransform){FillRectWithMask(aDest,aDestRect,aColor,aOptions,aMask,aMaskTransform);}staticvoidDrawSurface(gfx::DrawTarget*aDest,constgfx::Polygon&aPolygon,constgfx::Rect&aClipRect,constgfx::Color&aColor,constgfx::DrawOptions&aOptions,gfx::SourceSurface*aMask,constgfx::Matrix*aMaskTransform){RefPtr<Path>path=BuildPathFromPolygon(aDest,aPolygon);FillPathWithMask(aDest,path,aClipRect,aColor,aOptions,aMask,aMaskTransform);}staticvoidDrawTextureSurface(gfx::DrawTarget*aDest,constgfx::Rect&aDestRect,constgfx::Rect&/* aClipRect */,gfx::SourceSurface*aSource,gfx::SamplingFilteraSamplingFilter,constgfx::DrawOptions&aOptions,ExtendModeaExtendMode,gfx::SourceSurface*aMask,constgfx::Matrix*aMaskTransform,constMatrix*aSurfaceTransform){FillRectWithMask(aDest,aDestRect,aSource,aSamplingFilter,aOptions,aExtendMode,aMask,aMaskTransform,aSurfaceTransform);}staticvoidDrawTextureSurface(gfx::DrawTarget*aDest,constgfx::Polygon&aPolygon,constgfx::Rect&aClipRect,gfx::SourceSurface*aSource,gfx::SamplingFilteraSamplingFilter,constgfx::DrawOptions&aOptions,ExtendModeaExtendMode,gfx::SourceSurface*aMask,constgfx::Matrix*aMaskTransform,constMatrix*aSurfaceTransform){RefPtr<Path>path=BuildPathFromPolygon(aDest,aPolygon);FillPathWithMask(aDest,path,aClipRect,aSource,aSamplingFilter,aOptions,aExtendMode,aMask,aMaskTransform,aSurfaceTransform);}template<typenameGeometry>staticvoidDrawSurfaceWithTextureCoords(gfx::DrawTarget*aDest,constGeometry&aGeometry,constgfx::Rect&aDestRect,gfx::SourceSurface*aSource,constgfx::Rect&aTextureCoords,gfx::SamplingFilteraSamplingFilter,constgfx::DrawOptions&aOptions,gfx::SourceSurface*aMask,constgfx::Matrix*aMaskTransform){if(!aSource){gfxWarning()<<"DrawSurfaceWithTextureCoords problem "<<gfx::hexa(aSource)<<" and "<<gfx::hexa(aMask);return;}// Convert aTextureCoords into aSource's coordinate spacegfxRectsourceRect(aTextureCoords.x*aSource->GetSize().width,aTextureCoords.y*aSource->GetSize().height,aTextureCoords.width*aSource->GetSize().width,aTextureCoords.height*aSource->GetSize().height);// Floating point error can accumulate above and we know our visible region// is integer-aligned, so round it out.sourceRect.Round();// Compute a transform that maps sourceRect to aDestRect.Matrixmatrix=gfxUtils::TransformRectToRect(sourceRect,gfx::IntPoint::Truncate(aDestRect.x,aDestRect.y),gfx::IntPoint::Truncate(aDestRect.XMost(),aDestRect.y),gfx::IntPoint::Truncate(aDestRect.XMost(),aDestRect.YMost()));// Only use REPEAT if aTextureCoords is outside (0, 0, 1, 1).gfx::RectunitRect(0,0,1,1);ExtendModemode=unitRect.Contains(aTextureCoords)?ExtendMode::CLAMP:ExtendMode::REPEAT;DrawTextureSurface(aDest,aGeometry,aDestRect,aSource,aSamplingFilter,aOptions,mode,aMask,aMaskTransform,&matrix);}staticvoidSetupMask(constEffectChain&aEffectChain,DrawTarget*aDest,constIntPoint&aOffset,RefPtr<SourceSurface>&aMaskSurface,Matrix&aMaskTransform){if(aEffectChain.mSecondaryEffects[EffectTypes::MASK]){EffectMask*effectMask=static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());aMaskSurface=effectMask->mMaskTexture->AsSourceBasic()->GetSurface(aDest);if(!aMaskSurface){gfxWarning()<<"Invalid sourceMask effect";}MOZ_ASSERT(effectMask->mMaskTransform.Is2D(),"How did we end up with a 3D transform here?!");aMaskTransform=effectMask->mMaskTransform.As2D();aMaskTransform.PostTranslate(-aOffset.x,-aOffset.y);}}staticboolAttemptVideoScale(TextureSourceBasic*aSource,constSourceSurface*aSourceMask,gfx::FloataOpacity,CompositionOpaBlendMode,constTexturedEffect*aTexturedEffect,constMatrix&aNewTransform,constgfx::Rect&aRect,constgfx::Rect&aClipRect,DrawTarget*aDest,constDrawTarget*aBuffer){#ifdef MOZILLA_SSE_HAVE_CPUID_DETECTIONif(!mozilla::supports_ssse3())returnfalse;if(aNewTransform.IsTranslation())// unscaled painting should take the regular pathreturnfalse;if(aNewTransform.HasNonAxisAlignedTransform()||aNewTransform.HasNegativeScaling())returnfalse;if(aSourceMask||aOpacity!=1.0f)returnfalse;if(aBlendMode!=CompositionOp::OP_OVER&&aBlendMode!=CompositionOp::OP_SOURCE)returnfalse;IntRectdstRect;// the compiler should know a lot about aNewTransform at this point// maybe it can do some sophisticated optimization of the followingif(!aNewTransform.TransformBounds(aRect).ToIntRect(&dstRect))returnfalse;IntRectclipRect;if(!aClipRect.ToIntRect(&clipRect))returnfalse;if(!(aTexturedEffect->mTextureCoords==Rect(0.0f,0.0f,1.0f,1.0f)))returnfalse;if(aDest->GetFormat()==SurfaceFormat::R5G6B5_UINT16)returnfalse;uint8_t*dstData;IntSizedstSize;int32_tdstStride;SurfaceFormatdstFormat;if(aDest->LockBits(&dstData,&dstSize,&dstStride,&dstFormat)){// If we're not painting to aBuffer the clip will// be applied laterIntRectfillRect=dstRect;if(aDest==aBuffer){// we need to clip fillRect because LockBits ignores the clip on the aDestfillRect=fillRect.Intersect(clipRect);}fillRect=fillRect.Intersect(IntRect(IntPoint(0,0),aDest->GetSize()));IntPointoffset=fillRect.TopLeft()-dstRect.TopLeft();RefPtr<DataSourceSurface>srcSource=aSource->GetSurface(aDest)->GetDataSurface();DataSourceSurface::ScopedMapmapSrc(srcSource,DataSourceSurface::READ);ssse3_scale_data((uint32_t*)mapSrc.GetData(),srcSource->GetSize().width,srcSource->GetSize().height,mapSrc.GetStride()/4,((uint32_t*)dstData)+fillRect.x+(dstStride/4)*fillRect.y,dstRect.width,dstRect.height,dstStride/4,offset.x,offset.y,fillRect.width,fillRect.height);aDest->ReleaseBits(dstData);returntrue;}else#endif // MOZILLA_SSE_HAVE_CPUID_DETECTIONreturnfalse;}staticboolAttemptVideoConvertAndScale(TextureSource*aSource,constSourceSurface*aSourceMask,gfx::FloataOpacity,CompositionOpaBlendMode,constTexturedEffect*aTexturedEffect,constMatrix&aNewTransform,constgfx::Rect&aRect,constgfx::Rect&aClipRect,DrawTarget*aDest,constDrawTarget*aBuffer){#if defined(XP_WIN) && defined(_M_X64)// libyuv does not support SIMD scaling on win 64bit. See Bug 1295927.returnfalse;#endifWrappingTextureSourceYCbCrBasic*wrappingSource=aSource->AsWrappingTextureSourceYCbCrBasic();if(!wrappingSource)returnfalse;#ifdef MOZILLA_SSE_HAVE_CPUID_DETECTIONif(!mozilla::supports_ssse3())// libyuv requests SSSE3 for fast YUV conversion.returnfalse;if(aNewTransform.HasNonAxisAlignedTransform()||aNewTransform.HasNegativeScaling())returnfalse;if(aSourceMask||aOpacity!=1.0f)returnfalse;if(aBlendMode!=CompositionOp::OP_OVER&&aBlendMode!=CompositionOp::OP_SOURCE)returnfalse;IntRectdstRect;// the compiler should know a lot about aNewTransform at this point// maybe it can do some sophisticated optimization of the followingif(!aNewTransform.TransformBounds(aRect).ToIntRect(&dstRect))returnfalse;IntRectclipRect;if(!aClipRect.ToIntRect(&clipRect))returnfalse;if(!(aTexturedEffect->mTextureCoords==Rect(0.0f,0.0f,1.0f,1.0f)))returnfalse;if(aDest->GetFormat()==SurfaceFormat::R5G6B5_UINT16)returnfalse;if(aDest==aBuffer&&!clipRect.Contains(dstRect))returnfalse;if(!IntRect(IntPoint(0,0),aDest->GetSize()).Contains(dstRect))returnfalse;uint8_t*dstData;IntSizedstSize;int32_tdstStride;SurfaceFormatdstFormat;if(aDest->LockBits(&dstData,&dstSize,&dstStride,&dstFormat)){wrappingSource->ConvertAndScale(dstFormat,dstRect.Size(),dstData+ptrdiff_t(dstRect.x)*BytesPerPixel(dstFormat)+ptrdiff_t(dstRect.y)*dstStride,dstStride);aDest->ReleaseBits(dstData);returntrue;}else#endif // MOZILLA_SSE_HAVE_CPUID_DETECTIONreturnfalse;}voidBasicCompositor::DrawQuad(constgfx::Rect&aRect,constgfx::IntRect&aClipRect,constEffectChain&aEffectChain,gfx::FloataOpacity,constgfx::Matrix4x4&aTransform,constgfx::Rect&aVisibleRect){DrawGeometry(aRect,aRect,aClipRect,aEffectChain,aOpacity,aTransform,aVisibleRect,true);}voidBasicCompositor::DrawPolygon(constgfx::Polygon&aPolygon,constgfx::Rect&aRect,constgfx::IntRect&aClipRect,constEffectChain&aEffectChain,gfx::FloataOpacity,constgfx::Matrix4x4&aTransform,constgfx::Rect&aVisibleRect){DrawGeometry(aPolygon,aRect,aClipRect,aEffectChain,aOpacity,aTransform,aVisibleRect,false);}template<typenameGeometry>voidBasicCompositor::DrawGeometry(constGeometry&aGeometry,constgfx::Rect&aRect,constgfx::IntRect&aClipRect,constEffectChain&aEffectChain,gfx::FloataOpacity,constgfx::Matrix4x4&aTransform,constgfx::Rect&aVisibleRect,constboolaEnableAA){RefPtr<DrawTarget>buffer=mRenderTarget->mDrawTarget;// For 2D drawing, |dest| and |buffer| are the same surface. For 3D drawing,// |dest| is a temporary surface.RefPtr<DrawTarget>dest=buffer;AutoRestoreTransformautoRestoreTransform(dest);MatrixnewTransform;RecttransformBounds;Matrix4x4new3DTransform;IntPointoffset=mRenderTarget->GetOrigin();if(aTransform.Is2D()){newTransform=aTransform.As2D();}else{// Create a temporary surface for the transform.dest=Factory::CreateDrawTarget(gfxVars::ContentBackend(),RoundedOut(aRect).Size(),SurfaceFormat::B8G8R8A8);if(!dest){return;}dest->SetTransform(Matrix::Translation(-aRect.x,-aRect.y));// Get the bounds post-transform.transformBounds=aTransform.TransformAndClipBounds(aRect,Rect(offset.x,offset.y,buffer->GetSize().width,buffer->GetSize().height));transformBounds.RoundOut();if(transformBounds.IsEmpty()){return;}newTransform=Matrix();// When we apply the 3D transformation, we do it against a temporary// surface, so undo the coordinate offset.new3DTransform=aTransform;new3DTransform.PreTranslate(aRect.x,aRect.y,0);}// XXX the transform is probably just an integer offset so this whole// business here is a bit silly.RecttransformedClipRect=buffer->GetTransform().TransformBounds(Rect(aClipRect));buffer->PushClipRect(Rect(aClipRect));newTransform.PostTranslate(-offset.x,-offset.y);buffer->SetTransform(newTransform);RefPtr<SourceSurface>sourceMask;MatrixmaskTransform;if(aTransform.Is2D()){SetupMask(aEffectChain,dest,offset,sourceMask,maskTransform);}CompositionOpblendMode=CompositionOp::OP_OVER;if(Effect*effect=aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get()){blendMode=static_cast<EffectBlendMode*>(effect)->mBlendMode;}constAntialiasModeaaMode=aEnableAA?AntialiasMode::DEFAULT:AntialiasMode::NONE;DrawOptionsdrawOptions(aOpacity,blendMode,aaMode);switch(aEffectChain.mPrimaryEffect->mType){caseEffectTypes::SOLID_COLOR:{EffectSolidColor*effectSolidColor=static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get());boolunboundedOp=!IsOperatorBoundByMask(blendMode);if(unboundedOp){dest->PushClipRect(aRect);}DrawSurface(dest,aGeometry,aRect,effectSolidColor->mColor,drawOptions,sourceMask,&maskTransform);if(unboundedOp){dest->PopClip();}break;}caseEffectTypes::RGB:{TexturedEffect*texturedEffect=static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());TextureSourceBasic*source=texturedEffect->mTexture->AsSourceBasic();if(source&&texturedEffect->mPremultiplied){// we have a fast path for video hereif(source->mFromYCBCR&&AttemptVideoConvertAndScale(texturedEffect->mTexture,sourceMask,aOpacity,blendMode,texturedEffect,newTransform,aRect,transformedClipRect,dest,buffer)){// we succeeded in convert and scaling}elseif(source->mFromYCBCR&&!source->GetSurface(dest)){gfxWarning()<<"Failed to get YCbCr to rgb surface.";}elseif(source->mFromYCBCR&&AttemptVideoScale(source,sourceMask,aOpacity,blendMode,texturedEffect,newTransform,aRect,transformedClipRect,dest,buffer)){// we succeeded in scaling}else{DrawSurfaceWithTextureCoords(dest,aGeometry,aRect,source->GetSurface(dest),texturedEffect->mTextureCoords,texturedEffect->mSamplingFilter,drawOptions,sourceMask,&maskTransform);}}elseif(source){SourceSurface*srcSurf=source->GetSurface(dest);if(srcSurf){RefPtr<DataSourceSurface>srcData=srcSurf->GetDataSurface();// Yes, we re-create the premultiplied data every time.// This might be better with a cache, eventually.RefPtr<DataSourceSurface>premultData=gfxUtils::CreatePremultipliedDataSurface(srcData);DrawSurfaceWithTextureCoords(dest,aGeometry,aRect,premultData,texturedEffect->mTextureCoords,texturedEffect->mSamplingFilter,drawOptions,sourceMask,&maskTransform);}}else{gfxDevCrash(LogReason::IncompatibleBasicTexturedEffect)<<"Bad for basic with "<<texturedEffect->mTexture->Name()<<" and "<<gfx::hexa(sourceMask);}break;}caseEffectTypes::YCBCR:{MOZ_CRASH("Can't (easily) support component alpha with BasicCompositor!");break;}caseEffectTypes::RENDER_TARGET:{EffectRenderTarget*effectRenderTarget=static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get());RefPtr<BasicCompositingRenderTarget>surface=static_cast<BasicCompositingRenderTarget*>(effectRenderTarget->mRenderTarget.get());RefPtr<SourceSurface>sourceSurf=surface->mDrawTarget->Snapshot();DrawSurfaceWithTextureCoords(dest,aGeometry,aRect,sourceSurf,effectRenderTarget->mTextureCoords,effectRenderTarget->mSamplingFilter,drawOptions,sourceMask,&maskTransform);break;}caseEffectTypes::COMPONENT_ALPHA:{MOZ_CRASH("Can't (easily) support component alpha with BasicCompositor!");break;}default:{MOZ_CRASH("Invalid effect type!");break;}}if(!aTransform.Is2D()){dest->Flush();RefPtr<SourceSurface>destSnapshot=dest->Snapshot();SetupMask(aEffectChain,buffer,offset,sourceMask,maskTransform);if(sourceMask){RefPtr<DrawTarget>transformDT=dest->CreateSimilarDrawTarget(IntSize::Truncate(transformBounds.width,transformBounds.height),SurfaceFormat::B8G8R8A8);new3DTransform.PostTranslate(-transformBounds.x,-transformBounds.y,0);if(transformDT&&transformDT->Draw3DTransformedSurface(destSnapshot,new3DTransform)){RefPtr<SourceSurface>transformSnapshot=transformDT->Snapshot();// Transform the source by it's normal transform, and then the inverse// of the mask transform so that it's in the mask's untransformed// coordinate space.MatrixsourceTransform=newTransform;sourceTransform.PostTranslate(transformBounds.TopLeft());MatrixinverseMask=maskTransform;inverseMask.Invert();sourceTransform*=inverseMask;SurfacePatternsource(transformSnapshot,ExtendMode::CLAMP,sourceTransform);buffer->PushClipRect(transformBounds);// Mask in the untransformed coordinate space, and then transform// by the mask transform to put the result back into destination// coords.buffer->SetTransform(maskTransform);buffer->MaskSurface(source,sourceMask,Point(0,0));buffer->PopClip();}}else{buffer->Draw3DTransformedSurface(destSnapshot,new3DTransform);}}buffer->PopClip();}voidBasicCompositor::ClearRect(constgfx::Rect&aRect){mRenderTarget->mDrawTarget->ClearRect(aRect);}voidBasicCompositor::BeginFrame(constnsIntRegion&aInvalidRegion,constgfx::IntRect*aClipRectIn,constgfx::IntRect&aRenderBounds,constnsIntRegion&aOpaqueRegion,gfx::IntRect*aClipRectOut/* = nullptr */,gfx::IntRect*aRenderBoundsOut/* = nullptr */){if(mIsPendingEndRemoteDrawing){// Force to end previous remote drawing.TryToEndRemoteDrawing(/* aForceToEnd */true);MOZ_ASSERT(!mIsPendingEndRemoteDrawing);}LayoutDeviceIntRectintRect(LayoutDeviceIntPoint(),mWidget->GetClientSize());IntRectrect=IntRect(0,0,intRect.width,intRect.height);LayoutDeviceIntRegioninvalidRegionSafe;// Sometimes the invalid region is larger than we want to draw.invalidRegionSafe.And(LayoutDeviceIntRegion::FromUnknownRegion(aInvalidRegion),intRect);mInvalidRegion=invalidRegionSafe;mInvalidRect=mInvalidRegion.GetBounds();if(aRenderBoundsOut){*aRenderBoundsOut=IntRect();}BufferModebufferMode=BufferMode::BUFFERED;if(mTarget){// If we have a copy target, then we don't have a widget-provided mDrawTarget (currently). Use a dummy// placeholder so that CreateRenderTarget() works. This is only used to create a new buffered// draw target that we composite into, then copy the results the destination.mDrawTarget=mTarget;bufferMode=BufferMode::BUFFER_NONE;}else{// StartRemoteDrawingInRegion can mutate mInvalidRegion.mDrawTarget=mWidget->StartRemoteDrawingInRegion(mInvalidRegion,&bufferMode);if(!mDrawTarget){return;}mInvalidRect=mInvalidRegion.GetBounds();if(mInvalidRect.IsEmpty()){mWidget->EndRemoteDrawingInRegion(mDrawTarget,mInvalidRegion);return;}}if(!mDrawTarget||mInvalidRect.IsEmpty()){return;}LayoutDeviceIntRectclearRect;if(!aOpaqueRegion.IsEmpty()){LayoutDeviceIntRegionclearRegion=mInvalidRegion;clearRegion.SubOut(LayoutDeviceIntRegion::FromUnknownRegion(aOpaqueRegion));clearRect=clearRegion.GetBounds();}else{clearRect=mInvalidRect;}// Prevent CreateRenderTargetForWindow from clearing unwanted area.gfxUtils::ClipToRegion(mDrawTarget,mInvalidRegion.ToUnknownRegion());// Setup an intermediate render target to buffer all compositing. We will// copy this into mDrawTarget (the widget), and/or mTarget in EndFrame()RefPtr<CompositingRenderTarget>target=CreateRenderTargetForWindow(mInvalidRect,clearRect,bufferMode);mDrawTarget->PopClip();if(!target){if(!mTarget){mWidget->EndRemoteDrawingInRegion(mDrawTarget,mInvalidRegion);}return;}SetRenderTarget(target);// We only allocate a surface sized to the invalidated region, so we need to// translate future coordinates.mRenderTarget->mDrawTarget->SetTransform(Matrix::Translation(-mRenderTarget->GetOrigin()));gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget,mInvalidRegion.ToUnknownRegion());if(aRenderBoundsOut){*aRenderBoundsOut=rect;}if(aClipRectIn){mRenderTarget->mDrawTarget->PushClipRect(Rect(*aClipRectIn));}else{mRenderTarget->mDrawTarget->PushClipRect(Rect(rect));if(aClipRectOut){*aClipRectOut=rect;}}}voidBasicCompositor::EndFrame(){Compositor::EndFrame();// Pop aClipRectIn/bounds rectmRenderTarget->mDrawTarget->PopClip();if(gfxPrefs::WidgetUpdateFlashing()){floatr=float(rand())/RAND_MAX;floatg=float(rand())/RAND_MAX;floatb=float(rand())/RAND_MAX;// We're still clipped to mInvalidRegion, so just fill the bounds.mRenderTarget->mDrawTarget->FillRect(IntRectToRect(mInvalidRegion.GetBounds()).ToUnknownRect(),ColorPattern(Color(r,g,b,0.2f)));}// Pop aInvalidregionmRenderTarget->mDrawTarget->PopClip();TryToEndRemoteDrawing();}voidBasicCompositor::TryToEndRemoteDrawing(boolaForceToEnd){if(mIsDestroyed||!mRenderTarget){return;}// It it is not a good timing for EndRemoteDrawing, defter to call it.if(!aForceToEnd&&!mTarget&&NeedsToDeferEndRemoteDrawing()){mIsPendingEndRemoteDrawing=true;constuint32_tretryMs=2;RefPtr<BasicCompositor>self=this;RefPtr<Runnable>runnable=NS_NewRunnableFunction("layers::BasicCompositor::TryToEndRemoteDrawing",[self](){self->TryToEndRemoteDrawing();});MessageLoop::current()->PostDelayedTask(runnable.forget(),retryMs);return;}if(mRenderTarget->mDrawTarget!=mDrawTarget){// Note: Most platforms require us to buffer drawing to the widget surface.// That's why we don't draw to mDrawTarget directly.RefPtr<SourceSurface>source;if(mRenderTarget->mDrawTarget!=mDrawTarget){source=mWidget->EndBackBufferDrawing();}else{source=mRenderTarget->mDrawTarget->Snapshot();}RefPtr<DrawTarget>dest(mTarget?mTarget:mDrawTarget);nsIntPointoffset=mTarget?mTargetBounds.TopLeft():nsIntPoint();// The source DrawTarget is clipped to the invalidation region, so we have// to copy the individual rectangles in the region or else we'll draw blank// pixels.for(autoiter=mInvalidRegion.RectIter();!iter.Done();iter.Next()){constLayoutDeviceIntRect&r=iter.Get();dest->CopySurface(source,IntRect(r.x,r.y,r.width,r.height)-mRenderTarget->GetOrigin(),IntPoint(r.x,r.y)-offset);}}if(aForceToEnd||!mTarget){mWidget->EndRemoteDrawingInRegion(mDrawTarget,mInvalidRegion);}mDrawTarget=nullptr;mRenderTarget=nullptr;mIsPendingEndRemoteDrawing=false;}boolBasicCompositor::NeedsToDeferEndRemoteDrawing(){MOZ_ASSERT(mDrawTarget);MOZ_ASSERT(mRenderTarget);if(mTarget||mRenderTarget->mDrawTarget==mDrawTarget){returnfalse;}returnmWidget->NeedsToDeferEndRemoteDrawing();}voidBasicCompositor::FinishPendingComposite(){TryToEndRemoteDrawing(/* aForceToEnd */true);}}// namespace layers}// namespace mozilla